#include "console.h"
#include "string.h"
#include "queue.h"
#include "stdio.h"
#include "ctype.h"
#include "shell.h"
#include "scheduler.h"
#include "libasm.h"
#include "command.h"

/* Arreglo de consolas */
static consoleT tty[CONSOLES_COUNT];

/* Indica la consola que esta activa */
static unsigned int active_tty = 0;


void
init_tty(void)
{
	int i;
	for (i = 0; i < CONSOLES_COUNT; i++)
		tty[i].exists = 0;
}

int
new_console( unsigned int index, unsigned int cursor,int active)
{
	int i;

	if (index >= CONSOLES_COUNT || tty[index].exists == 1)
		return 0;

	/* Desactivar el video de la consola activa si hubiese una */
	if (tty[active_tty].exists == 1)
		disable_video(&(tty[active_tty].v));

	/* Marcarla como consola activa y lanzar un sheel en ella */
	active_tty = index;

	/* Inicializar el manejador de video */
	init_video(&(tty[index].v), cursor, active);

	/* Inicializar el keyboardT */
	/*init_keyboard(&c.k);*/
	tty[index].k.scancodes = new_queue(tty[index].k.scancode_array,
			MAX_SCANCODES, 1);
	tty[index].k.modifiers = 0;

	/* Incializar el buffer */
	init_buffer(&(tty[index].b));

	/* Indicar que existe */
	tty[index].exists = 1;

	/* Dibujarle la linea de abajo */
	for (i=0; i<80; i++)
	{
		set_cursor_pos(&(tty[index].v), 80*23 + i);
		write_video(&(tty[index].v), 205 /* = */, WHITE_TXT);
	}
	set_cursor_pos(&(tty[index].v), 0);

	/* Resetear luces de teclado */
	setLights(0);

	/* Cargar el shell en la consola */
	return new_task(shell, 0, NULL, 0);
}

videoT *
get_console_video(unsigned int index)
{
	if (index >= CONSOLES_COUNT || tty[index].exists == 0)
		return NULL;
	return &(tty[index].v);
}

consoleT *
get_console_by_index(unsigned int index)
{
	if (index >= CONSOLES_COUNT || tty[index].exists == 0)
		return NULL;
	return &(tty[index]);
}

int
sc_enqueue(unsigned char key)
{
	return enqueue(&(tty[active_tty].k.scancodes), &key);
}


int
sc_dequeue(unsigned char *key)
{
	return dequeue(&(tty[active_tty].k.scancodes), key);
}



int
sc_queue_is_empty(void)
{
	return queue_is_empty(&(tty[active_tty].k.scancodes));
}

int
get_keyboard_modifiers(void)
{
	return tty[active_tty].k.modifiers;
}

void
set_keyboard_modifiers(int modifiers)
{
	tty[active_tty].k.modifiers = modifiers;
}

int
sc_queue_is_full(void)
{
	return queue_is_full(&(tty[active_tty].k.scancodes));
}

int
switch_console(int index)
{
	eoi();

	if (index == -1)
		return 1;

	if (index == active_tty && task_exists("shell", active_tty) != 0)
		return 1;

	if (index >= CONSOLES_COUNT)
		return 0;

	/* Si la consola no existe crearla */
	if (tty[index].exists == 0)
		return new_console(index, 0, 1);

	/* Pasar a memoria de video secundaria la terminal que estaba
	 * activa */
	disable_video(&(tty[active_tty].v));

	active_tty = index;

	/* Pasar a memoria de video primaria la terminal que esta ahora
	 * activa */
	enable_video(&(tty[active_tty].v));

	/* Setear luces de teclado */
	setLights(tty[active_tty].k.modifiers);

	if (!task_exists("shell", active_tty) != 0)
		return new_task(shell, 0, NULL, 0);

	return 1;
}

int
console_exists(unsigned int index)
{
	if (index >= CONSOLES_COUNT)
		return 0;

	return tty[index].exists;
}


consoleT *
get_active_tty(void)
{
	return &tty[active_tty];
}

unsigned int
get_active_tty_num(void)
{
	return active_tty;
}

int
tty_is_empty(void)
{
	int i;

	for (i = 0; i < CONSOLES_COUNT; i++)
		if (tty[i].exists)
			return 1;
	return 0;
}



int
free_console(unsigned int index)
{
	if (index >= CONSOLES_COUNT)
		return 0;

	tty[index].exists = 0;
	return 1;
}


void
init_buffer(bufferT *b)
{
	(*b).dim = (*b).begin = (*b).pos = 0;
}

int
buffer_insert(consoleT *c, int key)
{
	static char aux[2 * BUFFER_LENGTH];
	char *tmp;
	int oldpos;

	oldpos = c->b.pos;

	/* Chequear si hay lugar en el buffer */
	if(c->b.dim >= BUFFER_LENGTH - 1 && key == '\n')
	{
		putchar('\n');
		c->b.vec[c->b.pos] = key;
		c->b.dim++;
		c->b.pos++;
		return 0;
	}

	if (c->b.dim >= BUFFER_LENGTH - 1)
	{
		return 0;
	}

	/* Chequear si se debe shiftear el vector */
	if (c->b.pos < c->b.dim)
	{
		if ( key != '\n' )
		{
			/* Shiftear el buffer */
			memcpy(aux, c->b.vec + c->b.pos,
			c->b.dim - c->b.pos);
			memcpy(c->b.vec + c->b.pos + 1, aux,
			c->b.dim - c->b.pos);

			/* Shiftear el video*/
			tmp = get_active_mem_video(&(c->v));
			memcpy(aux, tmp + BYTES_PER_CELL * c->v.cursor,
					(c->b.dim - c->b.pos) * 2);
			memcpy(tmp + BYTES_PER_CELL * (c->v.cursor + 1), aux,
			      	  (c->b.dim - c->b.pos) * 2);
		}
		else
			c->b.pos = c->b.dim;
	}

	/* Insertar el caracter y avanzar los cursores */
	c->b.vec[c->b.pos] = key;
	c->b.dim++;
	c->b.pos++;

	/* Si el caracter es imprimible, lo pongo en el video */
	if ( key == '\n' )
	{
		/* Avanzo a la proxima linea */
		putchar('\n');
	}
	else if (isprint(key))
	{
		putchar(key);

		/* Actualizo el cursor de video */
		set_cursor_pos(&(c->v), c->v.cursor - oldpos + c->b.pos - 1);
	}


	return 1;
}

void
buffer_delete(consoleT *c)
{
	static char aux[BUFFER_LENGTH];
	unsigned int cursor;

	if (c->b.pos == 0)
		return;

	/* Chequear si se debe eliminar algo */
	if (c->b.pos < c->b.dim)
	{
		char *tmp;
		/* shiftear el buffer */
		memcpy(aux, c->b.vec + c->b.pos, c->b.dim - c->b.pos);
		memcpy(c->b.vec + c->b.pos - 1, aux, c->b.dim - c->b.pos);

		/* shiftear la memoria de video */
		tmp = get_active_mem_video(&(c->v));
		memcpy(tmp + BYTES_PER_CELL * (c->v.cursor - 1),
				tmp + BYTES_PER_CELL * c->v.cursor,
			        (c->b.dim - c->b.pos) * BYTES_PER_CELL);
	}
	cursor = get_cursor_pos(&(c->v));
	set_cursor_pos(&(c->v), c->v.cursor - c->b.pos + c->b.dim - 1);
	write_video(&(c->v), ' ', WHITE_TXT);
	set_cursor_pos(&(c->v), cursor);

	c->b.pos--;
	c->b.dim--;
	set_cursor_pos(&(c->v),c->v.cursor - 1);
}

